home *** CD-ROM | disk | FTP | other *** search
- From: genrad!masscomp!tektronix!tekig4!bradn
- Subject: scpp - a selective C preprocessor (Part 2 of 2)
- Newsgroups: mod.sources
- Approved: jpn@panda.UUCP
-
- Mod.sources: Volume 3, Issue 14
- Submitted by: decvax!tektronix!tekig4!bradn
-
-
- This is the second half of scpp. The first half contains a brief explanation
- of the purpose of the program.
-
- Brad Needham
- Tektronix, Inc.
- ...decvax!tektronix!tekig4!bradn
-
- -------- cut along the dashed line -----
- #! /bin/sh
- # This is a shell archive, meaning:
- # 1. Remove everything above the #! /bin/sh line.
- # 2. Save the resulting text in a file.
- # 3. Execute the file with /bin/sh (not csh) to create the files:
- # parse.y
- # scpp.c
- # scpp.h
- # This archive created: Thu Sep 19 12:35:40 1985
- export PATH; PATH=/bin:$PATH
- echo shar: extracting "'parse.y'" '(10067 characters)'
- if test -f 'parse.y'
- then
- echo shar: will not over-write existing file "'parse.y'"
- else
- sed 's/^ X//' << \SHAR_EOF > 'parse.y'
- X/*
- X * parse.y - #if parser for the selective C preprocessor, scpp.
- X *
- X * Copyright (c) 1985 by
- X * Tektronix, Incorporated Beaverton, Oregon 97077
- X * All rights reserved.
- X *
- X * Permission is hereby granted for personal, non-commercial
- X * reproduction and use of this program, provided that this
- X * notice and all copyright notices are included in any copy.
- X */
- X
- X%term MUL /* * */
- X%term DIV /* / */
- X%term MOD /* % */
- X%term PLUS /* + */
- X%term MINUS /* - */
- X%term LS /* << */
- X%term RS /* >> */
- X%term AND /* & */
- X%term OR /* | */
- X%term ER /* ^ */
- X%term LT /* < */
- X%term LE /* <= */
- X%term GT /* > */
- X%term GE /* >= */
- X%term EQ /* == */
- X%term NE /* != */
- X%term ANDAND /* && */
- X%term OROR /* || */
- X%term CM /* , (comma) */
- X%term QUEST /* ? */
- X%term COLON /* : */
- X%term NOT /* ! */
- X%term COMPL /* ~ */
- X%term LP /* ( */
- X%term RP /* ) */
- X%term INT /* an integer */
- X%term FLOAT /* a float */
- X%term IDENT /* a identifier */
- X%term QUOTE /* ' (apostrophe) */
- X%term DQUOTE /* " */
- X%term BACKS /* \ (backslash) */
- X%term OPENC /* open comment sequence */
- X%term CLOSEC /* close comment sequence */
- X%term WHITE /* whitespace */
- X%term NL /* newline */
- X%term QNL /* escaped (quoted) newline */
- X%term COMMENT /* a comment */
- X%term OTHER /* anything else */
- X%term STRING /* a double-quote enclosed string constant */
- X%term CHARS /* a single-quote enclosed char constant */
- X%term POUNDLINE /*
- X * The initial '#' of a preprocessor directive
- X * (as opposed to a normal '#', which is of type OTHER).
- X */
- X%term DEFMAC /* an uninterpreted 'defined(x)' invocation */
- X
- X%left CM
- X%right QUEST COLON
- X%left OROR
- X%left ANDAND
- X%left OR
- X%left ER
- X%left AND
- X%left EQ NE
- X%left LT LE GE GT
- X%left LS RS
- X%left PLUS MINUS
- X%left MUL DIV MOD
- X%right NOT COMPL
- X%left LP
- X
- X%union {
- X int intval; /* yacc stack entries */
- X struct anode *lexval; /* everything in this file */
- X}
- X
- X%type <lexval> exp e term
- X%type <lexval> MUL DIV MOD PLUS MINUS LS RS AND OR ER LT LE GT GE EQ NE
- X ANDAND OROR CM QUEST COLON NOT COMPL LP RP INT FLOAT IDENT
- X QUOTE DQUOTE BACKS OPENC CLOSEC WHITE NL QNL COMMENT OTHER
- X STRING CHARS POUNDLINE DEFMAC
- X
- X%{
- X# include "scpp.h"
- X
- X/*
- X * struct anode - the structure used to pass strings.
- X * Allocated by mknode();
- X * Deallocated by freenode().
- X * The string described will be in pend[] and is NOT NULL-TERMINATED.
- X */
- X
- Xstruct anode {
- X int an_val; /*
- X * lexical (token) value of this string.
- X * A value of 0 == this node is free.
- X */
- X int an_ifval; /* integer result of this expression */
- X};
- X
- X# define NODESIZ 100 /* max number of nodes in a #if expresssion */
- Xstruct anode nodepool[NODESIZ];
- X
- Xstruct anode *mknode();
- X
- X# define NIL ((struct anode *) 0)
- X%}
- X
- X%start exp
- X%%
- Xexp: e
- X {
- X /*
- X * If the expression can be evaluated, set the result
- X */
- X
- X if ($1->an_val == INT) {
- X *curif |= $1->an_ifval != 0 ?
- X IF_TRUE : IF_FALSE;
- X }
- X freenode($1);
- X }
- X ;
- Xe: e MUL e
- X {
- X $1->an_ifval = $1->an_ifval * $3->an_ifval;
- X binop:
- X $$ = $1;
- X if ($1->an_val == INT && $3->an_val == INT) {
- X $$->an_val == INT;
- X } else {
- X $$->an_val = OTHER;
- X }
- X freenode($2);
- X freenode($3);
- X }
- X | e DIV e
- X {
- X if ($3->an_ifval == 0 && $3->an_val == INT) {
- X $3->an_val = OTHER;
- X warnf("division by zero in #if");
- X } else {
- X $1->an_ifval = $1->an_ifval / $3->an_ifval;
- X }
- X goto binop;
- X }
- X | e MOD e
- X {
- X if ($3->an_ifval == 0 && $3->an_val == INT) {
- X $3->an_val = OTHER;
- X warnf("mod by zero in #if");
- X } else {
- X $1->an_ifval = $1->an_ifval % $3->an_ifval;
- X }
- X goto binop;
- X }
- X | e PLUS e
- X {$1->an_ifval = $1->an_ifval + $3->an_ifval; goto binop;}
- X | e MINUS e
- X {$1->an_ifval = $1->an_ifval - $3->an_ifval; goto binop;}
- X | e LS e
- X {$1->an_ifval = $1->an_ifval << $3->an_ifval; goto binop;}
- X | e RS e
- X {$1->an_ifval = $1->an_ifval >> $3->an_ifval; goto binop;}
- X | e LT e
- X {$1->an_ifval = $1->an_ifval < $3->an_ifval; goto binop;}
- X | e GT e
- X {$1->an_ifval = $1->an_ifval > $3->an_ifval; goto binop;}
- X | e LE e
- X {$1->an_ifval = $1->an_ifval <= $3->an_ifval; goto binop;}
- X | e GE e
- X {$1->an_ifval = $1->an_ifval >= $3->an_ifval; goto binop;}
- X | e EQ e
- X {$1->an_ifval = $1->an_ifval == $3->an_ifval; goto binop;}
- X | e NE e
- X {$1->an_ifval = $1->an_ifval != $3->an_ifval; goto binop;}
- X | e AND e
- X {$1->an_ifval = $1->an_ifval & $3->an_ifval; goto binop;}
- X | e ER e
- X {$1->an_ifval = $1->an_ifval ^ $3->an_ifval; goto binop;}
- X | e OR e
- X {$1->an_ifval = $1->an_ifval | $3->an_ifval; goto binop;}
- X | e ANDAND e
- X {
- X /*
- X * since this is a logical AND, its value
- X * is known if either subexpression is false.
- X */
- X
- X $$ = $1;
- X if ($1->an_val == INT && $3->an_val == INT) {
- X /* both subexpressions are known */
- X $$->an_ifval = $1->an_ifval && $3->an_ifval;
- X } else {
- X if (($1->an_val == INT && !$1->an_ifval) ||
- X ($3->an_val == INT && !$3->an_ifval)) {
- X $$->an_val = INT;
- X $$->an_ifval = FALSE;
- X } else {
- X $$->an_val = OTHER;
- X }
- X }
- X freenode($2); freenode($3);
- X }
- X | e OROR e
- X {
- X /*
- X * since this is a logical OR, its value
- X * is known if either subexpression is true.
- X */
- X
- X $$ = $1;
- X if ($1->an_val == INT && $3->an_val == INT) {
- X /* both subexpressions are known */
- X $$->an_ifval = $1->an_ifval || $3->an_ifval;
- X } else {
- X if (($1->an_val == INT && $1->an_ifval) ||
- X ($3->an_val == INT && $3->an_ifval)) {
- X $$->an_val = INT;
- X $$->an_ifval = TRUE;
- X } else {
- X $$->an_val = OTHER;
- X }
- X }
- X freenode($2); freenode($3);
- X }
- X | e QUEST e COLON e
- X {
- X /*
- X * since this is an IF-ELSE, its value is known
- X * in some cases even if one subexpression is unknown.
- X */
- X
- X $$ = $1;
- X if ($1->an_val == INT) {
- X if ($1->an_ifval) {
- X $$->an_val = $3->an_val;
- X $$->an_ifval = $3->an_ifval;
- X } else {
- X $$->an_val = $5->an_val;
- X $$->an_ifval = $5->an_ifval;
- X }
- X } else {
- X $$->an_val = OTHER;
- X }
- X freenode($2); freenode($3); freenode($4);
- X freenode($5);
- X }
- X | e CM e
- X {
- X /*
- X * since this is a comma operator, the value of
- X * the first expression is irrelevant.
- X */
- X
- X $$ = $3;
- X freenode($1);
- X freenode($2);
- X }
- X | term
- X {$$ = $1;}
- X ;
- Xterm:
- X MINUS term
- X {
- X $2->an_ifval = -($2->an_ifval);
- X unop:
- X $$ = $2;
- X freenode($1);
- X }
- X | NOT term
- X {$2->an_ifval = !($2->an_ifval); goto unop;}
- X | COMPL term
- X {$2->an_ifval = ~($2->an_ifval); goto unop;}
- X | LP e RP
- X {
- X $$ = $2;
- X freenode($1); freenode($3);
- X }
- X | INT
- X {$$= $1;}
- X | IDENT
- X {/* an uninterpreted macro */ $$ = $1;}
- X | DEFMAC
- X {/* an uninterpreted 'defined(x)' invocation */ $$ = $1;}
- X ;
- X%%
- X
- Xyyerror(s)
- Xchar *s;
- X{
- X struct anode *anp;
- X
- X /* free all nodes */
- X
- X for (anp = &nodepool[0]; anp < &nodepool[NODESIZ]; anp++) {
- X anp->an_val = 0;
- X }
- X warnf("syntax error in #if");
- X}
- X
- X/*
- X * yylex() - the lexical analyzer for #if statements.
- X * yylex() reads from the stream of interpreted macros, skipping
- X * insignificant tokens, then sets yylval appropriately and returns
- X * the token number of the token.
- X */
- X
- Xint
- Xyylex()
- X{
- X int tok;
- X
- X
- X /*
- X * Skip whitespace, quoted newlines, and interpreted preprocessor
- X * directives;
- X * End-of-file or an unquoted newline marks the end of the parse;
- X * calculate the value of integers and character constants.
- X */
- X
- X if (!(yylval.lexval = mknode())) {
- X return(0);
- X }
- X
- X while ((tok = gintok()) == WHITE || tok == COMMENT || tok == QNL)
- X ;
- X
- X if (tok == 0 || tok == NL) {
- X freenode(yylval.lexval);
- X yylval.lexval = NIL;
- X return(0);
- X }
- X
- X yylval.lexval->an_val = tok;
- X if (tok == INT) {
- X yylval.lexval->an_ifval = inttok(curtext, nxtout);
- X } else if (tok == CHARS) {
- X yylval.lexval->an_val = INT;
- X yylval.lexval->an_ifval = chartok(curtext, nxtout);
- X }
- X return(yylval.lexval->an_val);
- X}
- X
- X/*
- X * inttok - convert integer token.
- X * Given the bounds of a token of type INT, return the value of that integer.
- X */
- X
- Xint
- Xinttok(s, e)
- Xchar *s, *e;
- X{
- X char *str; /* points to a (dynamically alloc'ed) copy of the tok */
- X char *cp;
- X int base; /* the radix of this integer */
- X int value; /* the value to return */
- X int digit; /* the value of the current digit */
- X
- X /*
- X * get a copy of the token (to remove ATTN bytes and null-terminate
- X * the string), and find out what the number base is.
- X */
- X
- X str = savtok(s, e);
- X cp = str;
- X if (*cp != '0') {
- X base = 10;
- X } else {
- X if (*cp && (*++cp == 'x' || *cp == 'X')) {
- X ++cp;
- X base = 16;
- X } else {
- X base = 8;
- X }
- X }
- X
- X /*
- X * convert the string
- X */
- X
- X value = 0;
- X for (;*cp; ++cp) {
- X if (*cp >= '0' && *cp <= '7') {
- X digit = (int)(*cp - '0');
- X } else if (*cp >= '8' && *cp <= '9' && base >= 10) {
- X digit = (int)(*cp - '0');
- X } else if (*cp >= 'a' && *cp <= 'f' && base == 16) {
- X digit = (int)(*cp - 'a') + 10;
- X } else if (*cp >= 'A' && *cp <= 'F' && base == 16) {
- X digit = (int)(*cp - 'A') + 10;
- X } else {
- X break;
- X }
- X value = value * base + digit;
- X }
- X
- X free(str);
- X return(value);
- X}
- X
- X/*
- X * chartok() - convert a character constant token.
- X * given the bounds of a character constant, return the integer value
- X * of that character constant.
- X */
- X
- Xint
- Xchartok(s, e)
- Xchar *s, *e;
- X{
- X char *str; /* (dynamically alloc'ed) copy of the token */
- X char *cp;
- X int value; /* value to return */
- X int cnt;
- X
- X
- X str = savtok(s, e);
- X
- X cp = str + 1;
- X if (*cp != '\\') {
- X value = (int) *cp;
- X } else if (*++cp == 'n') {
- X value = (int) '\n';
- X } else if (*cp == 't') {
- X value = (int) '\t';
- X } else if (*cp == 'b') {
- X value = (int) '\b';
- X/*--read the book to find out the other chars supported--*/
- X } else if (*cp >= '0' && *cp <= '7') {
- X for (value = 0, cnt = 3; cnt >= 1 && *cp >= '0' && *cp <= '7';
- X --cnt, ++cp) {
- X value = value * 8 + (int)(*cp - '0');
- X }
- X } else {
- X value = (int) *cp;
- X }
- X
- X free(str);
- X return(value);
- X}
- X
- Xstruct anode *
- Xmknode()
- X{
- X struct anode *anp;
- X
- X for (anp = &nodepool[0];
- X anp < &nodepool[NODESIZ] && anp->an_val != 0; anp++)
- X ;
- X if (anp >= &nodepool[NODESIZ]) {
- X warnf("#if expression too complex");
- X return(NIL);
- X }
- X anp->an_val = OTHER;
- X return(anp);
- X}
- X
- Xfreenode(n)
- Xstruct anode *n;
- X{
- X n->an_val = 0;
- X}
- SHAR_EOF
- if test 10067 -ne "`wc -c < 'parse.y'`"
- then
- echo shar: error transmitting "'parse.y'" '(should have been 10067 characters)'
- fi
- fi # end of overwriting check
- echo shar: extracting "'scpp.c'" '(14405 characters)'
- if test -f 'scpp.c'
- then
- echo shar: will not over-write existing file "'scpp.c'"
- else
- sed 's/^ X//' << \SHAR_EOF > 'scpp.c'
- X/*
- X * scpp.c - main processing for the selective C preprocessor, scpp.
- X *
- X * Copyright (c) 1985 by
- X * Tektronix, Incorporated Beaverton, Oregon 97077
- X * All rights reserved.
- X *
- X * Permission is hereby granted for personal, non-commercial
- X * reproduction and use of this program, provided that this
- X * notice and all copyright notices are included in any copy.
- X */
- X
- X#define VARS
- X# include <stdio.h>
- X# include "scpp.h"
- X# include "y.tab.h"
- X
- X/*
- X * actual[] - the array of actual parameters of the macro currently being
- X * interpreted.
- X */
- X
- Xstruct anactual {
- X char *aa_val; /*
- X * the value of this actual (a pointer to the null-
- X * terminator. see amacro.am_val in scpp.h).
- X */
- X char *aa_mem; /*
- X * points to the beginning of the aa_val string.
- X * Used to later free the value's memory.
- X */
- X};
- X#define ACTSIZ MAXPARMS
- Xstruct anactual actual[ACTSIZ];
- Xstruct anactual *actp; /* the next available slot in actual[] */
- X
- X
- X
- Xmain(argc, argv)
- Xint argc;
- Xchar **argv;
- X{
- X int tok; /* current token's value */
- X char *cp;
- X char *ep;
- X char **dp; /* where within dirlist to put the next directory */
- X struct amacro *np;
- X char *name; /* name of the current macro */
- X char *val; /* value of the current macro */
- X char *defmagic = "defined"; /* name of the 'defined()' macro */
- X struct amacro *magmac; /* (temp) slot for the magic macro */
- X
- X /*
- X * init all the global structures
- X */
- X
- X nxtout = &pend[0];
- X curfile = &filestk[-1];
- X nxtin = &istk[ISTKSIZ];
- X curif = &ifstk[-1];
- X
- X nxtfile = &catlist[0];
- X dp = &dirlist[0];
- X
- X /*
- X * setup the keyword symbols and the special macro, 'defined()'.
- X */
- X
- X ikeywords();
- X magmac = findmac(defmagic, defmagic + strlen(defmagic));
- X if (magmac->am_name) {
- X bomb("INTERNAL: 'defined()' macro slot in use");
- X }
- X magmac->am_name = defmagic;
- X magmac->am_npar = 1;
- X magmac->am_val = &magicval;
- X
- X while (++argv, --argc > 0) {
- X cp = *argv;
- X if (*cp == '-' && *(cp + 1) != '\0') {
- X switch(*++cp) {
- X case 'C':
- X savcom = TRUE;
- X break;
- X case 'I':
- X *dp++ = cp + 1;
- X break;
- X case 'M':
- X /*
- X * for each name in the list of whitespace-
- X * separated macro names,
- X * Setup a slot for that macro, but leave it
- X * undefined.
- X */
- X
- X while (*cp) {
- X while (*++cp == ' ' || *cp == '\t' ||
- X *cp == '\n')
- X ;
- X if (*cp == '\0') {
- X break;
- X }
- X for (name = cp; *cp != '\0' &&
- X *cp != ' ' && *cp != '\t' &&
- X *cp != '\n'; ++cp)
- X ;
- X
- X np = findmac(name, cp);
- X if (np->am_name == (char *) 0) {
- X np->am_name = savtok(name, cp);
- X np->am_npar = -1;
- X }
- X /* am_val is left as zero */
- X }
- X break;
- X case 'D':
- X for (name = ++cp; *cp != '\0' && *cp != '=';
- X ++cp)
- X ;
- X if (name == cp) {
- X warn("missing macro name in `%s'",
- X name - 2);
- X break;
- X }
- X
- X if (*cp == '\0') {
- X /*
- X * macro name with no definition.
- X * Define the name with no parameters
- X * and with a value of "1".
- X */
- X
- X defmac(name, cp, -1, "1");
- X } else {
- X /* macro + definition */
- X
- X for (*cp++ = '\0', val = cp;
- X *cp != '\0'; ++cp)
- X ;
- X defmac(name, name + strlen(name),
- X -1, val);
- X }
- X break;
- X default:
- X bomb("unknown switch `%c'", *cp);
- X }
- X } else {
- X *nxtfile++ = cp;
- X }
- X }
- X
- X if (nxtfile == &catlist[0]) {
- X *nxtfile++ = "-";
- X }
- X *nxtfile = (char *) 0;
- X nxtfile = &catlist[0];
- X
- X *dp++ = "/usr/include";
- X *dp = (char *) 0;
- X
- X /*
- X * prime the input stack and go,
- X * interpreting preprocessor directives along the way.
- X */
- X
- X pushfile(*nxtfile++, PF_NOLOOK, PF_NOHIDE);
- X do {
- X tok = gintok();
- X if (tok == POUNDLINE) {
- X tok = doctrl(curtext);
- X }
- X outpend(); /* even the 0 token needs to be flushed.
- X * Otherwise, incomplete comments at the end
- X * of the file would be destroyed.
- X */
- X } while (tok != 0);
- X writepend(); /* flush trailing output */
- X
- X if (curif >= &ifstk[0]) {
- X warnf("missing endif");
- X }
- X
- X exit(sawerror ? 1 : 0);
- X}
- X
- Xint
- Xgintok() /* get a token, interpreting macro's */
- X{
- X int tok; /* the current token's value */
- X struct amacro *mac; /* the current macro */
- X struct amacro *defsym; /* the macro being checked for 'defined()' */
- X char *mactext; /*
- X * the start of the invocation of a macro
- X * which has parameters.
- X */
- X char *start; /* the start of the current parameter */
- X int nest; /*
- X * current nesting level of parentheses.
- X * used to avoid misinterpreting commas within
- X * nested parens as parameter separators.
- X */
- X char *defident; /*
- X * The IDENT parameter for the magic macro,
- X * 'defined()' (dynamically alloc'ed).
- X * If gintok() is interpreting the magic macro,
- X * this variable is marked so that, during the
- X * parameter parsing, the first IDENT is saved
- X * here.
- X */
- X int parmgripe; /*
- X * "an error message about parameters of
- X * this macro has already been printed."
- X */
- X int i; /* an actual-parameter index */
- X char *cp; /* a temp pointer */
- X
- X /*
- X * special macro values (see scpp.h: struct amacro, field am_val):
- X * noval == a null macro value;
- X * oneval == a macro value of '1';
- X * zeroval == a macro value of '0';
- X */
- X
- X static char nv[2] = {'\0', '\0'};
- X static char *noval = &nv[1];
- X static char ov[3] = {'\0', '1', '\0'};
- X static char *oneval = &ov[2];
- X static char zv[3] = {'\0', '0', '\0'};
- X static char *zeroval = &zv[2];
- X
- X
- X tok = OTHER;
- X while (tok != DEFMAC && (tok = gtok()) != 0) {
- X if (tok == QUOTE || tok == DQUOTE) {
- X tok = gstrtok(tok);
- X }
- X if (tok != IDENT) {
- X return(tok);
- X }
- X
- X if ((mac = findmac(curtext, nxtout))->am_name == (char *) 0 ||
- X mac->am_val == (char *) 0) {
- X /* there is no macro by this name currently defined */
- X
- X return(tok);
- X }
- X
- X /*
- X * tally this interpretation
- X */
- X
- X ++ninterp;
- X
- X if (mac->am_npar < 0) {
- X /*
- X * the macro has no formal parameters.
- X * pushback the replacement text and continue.
- X */
- X
- X (void) dispose(curtext);
- X (void) pushmac(mac->am_val);
- X continue;
- X }
- X
- X /* this is a macro with formals */
- X
- X /*
- X * save the starting-point of the macro's text.
- X * Used for later disposal. The text is not disposed
- X * here in case the macro is a 'defined()' of some non--M'ed
- X * macro.
- X */
- X
- X mactext = curtext;
- X
- X /*
- X * collect the comma-separated actual parameters of the macro,
- X * ignoring commas within pairs of parens or within strings.
- X */
- X
- X parmgripe = FALSE;
- X actp = &actual[0];
- X nest = 0;
- X if (mac->am_val == &magicval) {
- X defident = &magicval;
- X } else {
- X defident = (char *) 0;
- X }
- X
- X if ((tok = nonwhite(gtok)) != LP) {
- X warnf("missing parenthesis in macro");
- X parmgripe = TRUE;
- X
- X /* pushback the erroneous token */
- X untok();
- X } else {
- X do {
- X /* collect one parameter */
- X
- X start = nxtout;
- X while ((tok = gtok())) {
- X if (tok == CM && nest == 0) {
- X break;
- X } else if (tok == RP) {
- X if (nest > 0) {
- X --nest;
- X } else if (nest == 0) {
- X break;
- X }
- X } else if (tok == LP) {
- X ++nest;
- X } else if (tok == QUOTE ||
- X tok == DQUOTE) {
- X tok = gstrtok(tok);
- X } else if (tok == IDENT &&
- X defident == &magicval) {
- X defident =
- X savtok(curtext, nxtout);
- X }
- X }
- X
- X /*
- X * Warn about too many parameters, otherwise,
- X * store the parameter in the format of
- X * a macro value.
- X */
- X
- X if ((actp - &actual[0]) >= mac->am_npar) {
- X if (!parmgripe) {
- X warnf("macro parameter mismatch");
- X parmgripe = TRUE;
- X }
- X } else {
- X cp = savtok(start - 1, curtext);
- X *cp = '\0';
- X actp->aa_mem = cp;
- X while (*++cp)
- X ;
- X actp->aa_val = cp;
- X ++actp;
- X }
- X } while (tok == CM);
- X if (tok != RP) {
- X if (!parmgripe) {
- X warnf("missing parenthesis in macro");
- X parmgripe = TRUE;
- X }
- X }
- X }
- X
- X /*
- X * If there are too few actual parameters, fill out the
- X * list with null values.
- X */
- X
- X while (actp - &actual[0] < mac->am_npar) {
- X if (!parmgripe) {
- X warnf("parameter mismatch");
- X parmgripe = TRUE;
- X }
- X actp->aa_val = noval;
- X actp->aa_mem = (char *) 0;
- X ++actp;
- X }
- X
- X /*
- X * replace the macro invocation with the value of the macro,
- X * replacing formal arguments with the corresponding actual.
- X */
- X
- X if ((cp = mac->am_val) == &magicval) {
- X /*
- X * This is the magic macro, "defined(x)".
- X * Interpret only if the parameter is a -M'ed
- X * macro and we are currently parsing a
- X * #if expression.
- X * Lookup the parameter (if any);
- X * If the parameter is -M'ed, pushback a '1' or '0',
- X * depending on whether the macro is defined.
- X */
- X
- X defsym = findmac(defident, defident + strlen(defident));
- X if (!defsym->am_name || !expparse) {
- X /*
- X * Leave the invocation of defined() untouched.
- X */
- X
- X curtext = mactext;
- X tok = DEFMAC;
- X } else {
- X (void) dispose(mactext);
- X if (defsym->am_val) {
- X (void) pushmac(oneval);
- X } else {
- X (void) pushmac(zeroval);
- X }
- X }
- X free(defident);
- X } else {
- X (void) dispose(mactext);
- X while (*(cp = pushmac(cp)) == ATTN) {
- X i = (int) (*--cp) - 1;
- X if (i < 0 || i >= mac->am_npar) {
- X warnf(
- X"INTERNAL: parameter number %d out of bounds", i);
- X } else {
- X (void) pushmac(actual[i].aa_val);
- X }
- X }
- X }
- X
- X /*
- X * free the actual parameters.
- X */
- X
- X while (--actp >= &actual[0]) {
- X if (actp->aa_mem) {
- X free(actp->aa_mem);
- X }
- X }
- X }
- X return(tok);
- X}
- X
- X/*
- X * gtok() - get a token without interpreting macros or preprocessor directives.
- X * This is the low-level lexical analyzer. It exists only because Lex's
- X * analyzer chokes on long comments.
- X */
- X
- Xint
- Xgtok()
- X{
- X int tok;
- X
- X
- X curtext = nxtout;
- X tok = xxlex();
- X if (tok == OPENC) {
- X while ((tok = xxlex()) != CLOSEC) {
- X if (tok == 0) {
- X warnf("unterminated comment");
- X return(0);
- X }
- X }
- X tok = COMMENT;
- X }
- X return(tok);
- X}
- X
- X/*
- X * gstrtok - get a string token. Given the token which starts a string
- X * or character constant (I.E. QUOTE or DQUOTE), collect the string token
- X * as if it had been recognised by the lexical analyzer as a single token.
- X */
- X
- Xint
- Xgstrtok(tok)
- Xint tok; /* token which started the quoted string */
- X{
- X int tok2; /* the next token's value */
- X char *qstrt; /* start of a string in pend[] */
- X
- X /*
- X * collect the string without interpreting
- X * macros. Allow \' and \" within strings.
- X * Newline or EOF terminate strings.
- X * Save and restore curtext so that on returning,
- X * curtext points to the beginning of the token.
- X */
- X
- X qstrt = curtext;
- X while ((tok2 = gtok()) != tok) {
- X if (tok2 == 0) {
- X /* unterminated quote */
- X curtext = qstrt;
- X return(0);
- X }
- X if (tok2 == NL) {
- X /* unterminated quote. pushback the newline */
- X
- X untok();
- X break;
- X }
- X if (tok2 == BACKS) {
- X if (gtok() == 0) {
- X /* unterminated quote */
- X curtext = qstrt;
- X return(0);
- X }
- X }
- X }
- X curtext = qstrt;
- X return(tok == DQUOTE ? STRING : CHARS);
- X}
- X
- X/*
- X * findmac - find a macro
- X * given the bounds of what might be a macro name (possibly containing ATTN
- X * bytes), return a pointer to the symbol table slot
- X * corresponding to that name.
- X */
- X
- Xstruct amacro *
- Xfindmac(name, last)
- Xchar *name; /* points to the beginning of the name. */
- Xchar *last; /* points to the char beyond the end of the name */
- X{
- X /*
- X * hash the first 8 chars of the name (less ATTN bytes) into an index;
- X * Use that index as a starting point for a linear search
- X * for either the matching slot or an empty slot.
- X */
- X
- X int idx;
- X char *cp;
- X char *tp;
- X int cnt;
- X struct amacro *np, *start;
- X
- X
- X for (idx = 0, cp = name, cnt = 0; cp < last && cnt < 8; ++cp) {
- X if (*cp == ATTN) {
- X ++cp;
- X } else {
- X idx += (int) *cp++ & 0xff;
- X ++cnt;
- X }
- X }
- X start = np = &sym[idx % SYMSIZ];
- X
- X while (np->am_name) {
- X /*
- X * compare the token at 'name' with the macro's name,
- X * skipping ATTN bytes and their associated codes.
- X */
- X
- X for (tp = name, cp = np->am_name; tp < last; ++tp) {
- X if (*tp == ATTN) {
- X ++tp;
- X continue;
- X }
- X if (*tp != *cp++) {
- X break;
- X }
- X }
- X if (tp == last) {
- X /* the names match */
- X break;
- X }
- X
- X if (++np >= &sym[SYMSIZ]) {
- X np = &sym[0];
- X }
- X if (np == start) {
- X bombf("symbol table overflow");
- X }
- X }
- X return(np);
- X}
- X
- X/*
- X * defmac - define a macro
- X */
- X
- Xdefmac(name, end, npar, val)
- Xchar *name; /* the start of the macro's name */
- Xchar *end; /* points to one char beyond the end of the name */
- Xint npar; /* # of parameters (-1 == none) */
- Xchar *val; /* the beginning of the value string */
- X{
- X char *cp;
- X struct amacro *np;
- X struct akeyword *kp;
- X char *malloc();
- X
- X
- X /*
- X * find the slot for the macro and give it a name if this is the
- X * first occurrence of this name.
- X */
- X
- X np = findmac(name, end);
- X if (!np->am_name) {
- X np->am_name = savtok(name, end);
- X } else {
- X /*
- X * Don't allow preprocessor keywords to be defined.
- X */
- X
- X if ((kp = findkey(np)) != (struct akeyword *) 0) {
- X warnf("redeclaration of keyword \"%s\"", kp->ak_name);
- X return;
- X }
- X
- X /*
- X * if the macro is currently defined (I.E. has a value),
- X * reject redefinitions of magic macros.
- X * compare the new and old values.
- X * If the value or number of parameters differs,
- X * print a warning and destroy the old value.
- X * If they are the same, do nothing (return).
- X */
- X
- X if (np->am_val) {
- X if (np->am_val == &magicval) {
- X warnf("cannot redefine implicit macro");
- X return;
- X }
- X cp = np->am_val;
- X while (*--cp)
- X ;
- X if (np->am_npar == npar && strcmp(cp + 1, val) == 0) {
- X return;
- X }
- X
- X warnf("redeclaration of \"%s\"", np->am_name);
- X free(cp);
- X }
- X }
- X
- X /*
- X * Set the new value and number of parameters.
- X * Put a null introduction on the value;
- X * Remember that am_val points to the *end* of the value.
- X */
- X
- X np->am_npar = npar;
- X
- X if (!(cp = malloc((unsigned) strlen(val) + 2))) {
- X bombf("out of memory");
- X }
- X *cp++ = '\0';
- X strcpy(cp, val);
- X np->am_val = cp + strlen(cp);
- X}
- X
- X/*
- X * savtok - given the limits of a token string,
- X * copy that string (less ATTN bytes) into a dynamically allocated buffer
- X * then return the buffer.
- X */
- X
- Xchar *
- Xsavtok(s, e)
- Xchar *s; /* first char of token */
- Xchar *e; /* points beyond the last char of token */
- X{
- X char *name; /* the text of the token -- the value to return */
- X char *cp;
- X char *malloc();
- X
- X if (!(name = malloc(e - s + 1))) {
- X bombf("out of memory");
- X }
- X
- X for (cp = name; s < e; ++s) {
- X if (*s == ATTN) {
- X ++s;
- X } else {
- X *cp++ = *s;
- X }
- X }
- X *cp = '\0';
- X
- X return(name);
- X}
- SHAR_EOF
- if test 14405 -ne "`wc -c < 'scpp.c'`"
- then
- echo shar: error transmitting "'scpp.c'" '(should have been 14405 characters)'
- fi
- fi # end of overwriting check
- echo shar: extracting "'scpp.h'" '(10614 characters)'
- if test -f 'scpp.h'
- then
- echo shar: will not over-write existing file "'scpp.h'"
- else
- sed 's/^ X//' << \SHAR_EOF > 'scpp.h'
- X
- X/*
- X * scpp.h - common declarations for the selective C preprocessor, scpp.
- X *
- X * Copyright (c) 1985 by
- X * Tektronix, Incorporated Beaverton, Oregon 97077
- X * All rights reserved.
- X *
- X * Permission is hereby granted for personal, non-commercial
- X * reproduction and use of this program, provided that this
- X * notice and all copyright notices are included in any copy.
- X */
- X
- X# define TRUE 1
- X# define FALSE 0
- X
- X/*
- X * sawerror - "some error was processed" If true, scpp exits non-zero.
- X * Set by the error printout routines, examined when exiting.
- X */
- X#ifdef VARS
- Xint sawerror;
- X#else
- Xextern int sawerror;
- X#endif
- X
- X# define BSIZE 512 /*
- X * # of bytes per read -- controls how quickly
- X * istk[] is consumed.
- X */
- X
- X/*
- X * PENDSIZ is a tunable parameter -- it is the largest number of characters
- X * which can be waiting to be output. This number sets a limit on:
- X * 1) the longest comment;
- X * 2) the largest invocation of a macro with parameters, i.e. the number
- X * of characters between the '(' and the ')'.
- X * 3) the longest preprocessor control line, e.g. #define....
- X * PENDSIZ also controls the input stack size, ISTK.
- X *
- X * Pend[] is the pending output buffer.
- X *
- X * Nxtout points to where within pend[] to put the next token scanned.
- X * Nxtout is advanced by questr() and quec(),
- X * the primitives for putting stuff in pend[],
- X * and is moved backward by dispose() and outpend(),
- X * the primitives for getting stuff out of pend[].
- X *
- X * Curtext points to the start of the text within pend[] of
- X * the current token. Set by gtok() and gintok().
- X * For anyone who uses gtok() or gintok() to get
- X * a token, the limits of the text of the resultant
- X * token are curtext and nxtout. (be aware that the
- X * text of anything in pend[] may contain imbedded
- X * ATTN bytes.)
- X */
- X
- X# define PENDSIZ 8000
- X# define PENDHIGH 512 /* highwater mark for flushing pend[] */
- X#ifdef VARS
- Xchar pend[PENDSIZ];
- Xchar *nxtout;
- Xchar *curtext;
- X#else
- Xextern char pend[];
- Xextern char *nxtout;
- Xchar *curtext;
- X#endif
- Xextern char *dispose();
- X#define outpend() (nxtout < &pend[PENDHIGH] ? 0 : writepend())
- X
- X/*
- X * filestk - the stack containing the state of the current file
- X */
- X
- Xstruct afile {
- X int af_fd; /* the open file's file-descriptor */
- X char *af_name; /* the name of the file (dynamic alloc) */
- X int af_line; /* the current line in the file */
- X int af_raw; /*
- X * "scanning unprocessed data rather than
- X * pushed-back data".
- X * Used to count input lines.
- X * Also used to prevent
- X * interpretation of "#if" expressions whose
- X * truth or falsehood does not depend on
- X * interpreting macros (e.g. #if '\377' > 0).
- X */
- X int af_hide; /*
- X * "do not output anything for this file."
- X * This file is the result of an uninterpreted
- X * "# include".
- X */
- X};
- X
- X#define FILESIZ 11 /* max # of include files + 1 (the original file) */
- X#ifdef VARS
- Xstruct afile filestk[FILESIZ];
- Xstruct afile *curfile; /* the current file. Initially = &filestk[-1] */
- X#else
- Xextern struct afile filestk[];
- Xextern struct afile *curfile;
- X#endif
- X
- X/*
- X * ISTKSIZ is the size of the input/pushback stack.
- X * It contains up to one block of data for each pending file plus
- X * one pending token.
- X * The input stack grows down from istk[ISTKSIZ - 1].
- X *
- X * Nxtin points to the next char to read from istk[].
- X * Characters are popped from the stack by nxtc()
- X * and are pushed back on the stack by unc() and
- X * pushmac().
- X */
- X
- X# define ISTKSIZ (FILESIZ * BSIZE + PENDSIZ)
- X#ifdef VARS
- Xchar istk[ISTKSIZ];
- Xchar *nxtin;
- X#else
- Xextern char istk[];
- Xextern char *nxtin;
- X#endif
- Xextern char nxtc();
- Xextern char *pushmac();
- X#define unc(c) (nxtin-- < &istk[0] ? over() : (*nxtin = c))
- X
- X/*
- X * ATTN appears in the input stack to notify nxtc() of some condition,
- X * in the output queue to notify dispose() or outpend() of some condition,
- X * or in the value of a macro to notify gintok() of some condition.
- X * ATTN means that the next byte contains a control code.
- X * Input control codes are:
- X * AT_EPUSH - end of pushed-back data. what follows has not been
- X * scanned before.
- X * AT_EBLK - end of block. read another block from the current file.
- X * Output control codes are:
- X * AT_OUTOFF - disable further output.
- X * AT_OUTON - enable output.
- X * Macro value control codes are formal parameter numbers and are not defined.
- X *
- X * note: to avoid breaking string operations and newline recognition,
- X * do not add an ATTN control code which has a value of '\0', '\\', or '\n'.
- X */
- X
- X#define ATTN '\376' /* this char must not appear in any file */
- X#define AT_EPUSH '\001'
- X#define AT_EBLK '\002'
- X
- X#define AT_OUTOFF '\006'
- X#define AT_OUTON '\007'
- X
- X/*
- X * Ninterp - number of interpretations. Incremented each time
- X * gintok() interprets a macro. Since there is no
- X * overflow detection, ninterp can be used only to
- X * see if some interpretation took place -- not to
- X * count the interpretations (e.g. "oldnint != ninterp"
- X * works, but "cnt = ninterp - oldnint" may fail).
- X * Used in conjunction with af_raw to prevent
- X * interpretation of #if's which are always true
- X * or false without any macro interpretation (e.g.
- X * "#if '\377' > 0").
- X */
- X
- X#ifdef VARS
- Xint ninterp;
- X#else
- Xextern int ninterp;
- X#endif
- X
- X/*
- X * Falsecnt - number of currently false #if's;
- X * Hidecnt - current number of uninterpreted #include's.
- X * Collectively, these variables are used to determine when
- X * to enable or disable output.
- X */
- X
- X#ifdef VARS
- Xint falsecnt;
- Xint hidecnt;
- X#else
- Xextern int falsecnt;
- Xextern int hidecnt;
- X#endif
- X
- X/*
- X * ifstk[] contains flags describing the state of all currently active #if's.
- X * curif points to the currently active #if within the stack.
- X * The stack grows upward, starting at ifstk[-1].
- X */
- X
- X#define IF_INIF '\001' /* "in the 'if' clause rather than 'else'" */
- X#define IF_TRUE '\002' /* "this if is currently true" */
- X#define IF_FALSE '\004' /* "this if is currently false" */
- X /* uninterpreted #if statements are neither true nor false. */
- X#define IFSIZ 100 /* maximum number of nested #if's */
- X#ifdef VARS
- Xchar ifstk[IFSIZ];
- Xchar *curif;
- X#else
- Xextern char ifstk[];
- Xextern char *curif;
- X#endif
- X
- X/*
- X * expparse - "currently parsing a #if expression".
- X * Used to prevent interpretation of the macro "defined()" outside
- X * #if expressions.
- X */
- X
- X#ifdef VARS
- Xint expparse;
- X#else
- Xextern int expparse;
- X#endif
- X
- X/*
- X * the next set of definitions are values of parameters to pushfile().
- X * PF_NOLOOK - the filename was given on the command line. Don't
- X * search any directories for it.
- X * PF_NODOT - the include filename was enclosed in '<' and '>'.
- X * Do not search the current directory (dot) for the it.
- X * PF_DOT - the include filename was enclosed in double-quotes.
- X *
- X * PF_HIDE - the file is not to be interpreted (I.e. is an include file).
- X * Do not output anything while processing this file.
- X * PF_NOHIDE - the file is to be interpreted.
- X */
- X
- X# define PF_NOLOOK (-1)
- X# define PF_NODOT 0
- X# define PF_DOT 1
- X
- X# define PF_HIDE TRUE
- X# define PF_NOHIDE FALSE
- X
- X/*
- X * savcom - "save comments and whitespace"
- X * If false, comments and leading and trailing whitespace are removed
- X * from interpreted macro definitions.
- X */
- X
- X#ifdef VARS
- Xint savcom;
- X#else
- Xextern int savcom;
- X#endif
- X
- X/*
- X * catlist - the list of files to process; I.E. the filenames from
- X * the command line. A zero pointer marks the end of the list.
- X * nxtfile - points to the next element of catlist[] to be processed.
- X */
- X
- X# define CLSIZ 100
- X#ifdef VARS
- Xchar *catlist[CLSIZ];
- Xchar **nxtfile;
- X#else
- Xextern char *catlist[];
- Xextern char **nxtfile;
- X#endif
- X
- X/*
- X * dirlist - the list of directories to search for an include file.
- X * I.E. all the -I directories from the command line + /usr/include.
- X * (the search of the current directory of the file is handled separately.)
- X * A zero pointer marks the end of the list.
- X */
- X
- X#define DLSIZ 100
- X#ifdef VARS
- Xchar *dirlist[DLSIZ];
- X#else
- Xextern char *dirlist[];
- X#endif
- X
- X/*
- X * The symbol table. All macros are stored in this table.
- X */
- X
- Xstruct amacro {
- X char *am_name; /*
- X * the name of this macro (dynamically allocated).
- X * An am_name value of 0 means this slot is empty.
- X * All macros to be interpreted are allocated slots
- X * before any files are scanned. #define and #undef
- X * do not allocate or free symbol-table slots.
- X */
- X int am_npar; /* number of parameters. -1 == no parameters. */
- X char *am_val; /*
- X * the value (replacement text) of the macro.
- X * (dynamically allocated.)
- X * An am_val value of 0 means that this macro is not
- X * currently defined.
- X *
- X * am_val points to the null-terminator of the
- X * replacement text. The replacement text is to be
- X * read backwards from (am_val - 1) until a null-
- X * terminator is found at the other end.
- X * An ATTN byte followed (well, preceeded if scanning
- X * forward) by a one-byte integer parameter number
- X * is replaced when expanding this macro by the
- X * corresponding actual parameter.
- X * To avoid breaking string operations on val strings,
- X * parameter numbers begin at 1 rather than 0
- X *
- X * A visual example may help:
- X * #define goop(name) hello there name people
- X * results in a sym[] slot containing:
- X *
- X * am_name:-------------|
- X * V
- X * goop\0
- X * am_npar: 1
- X * am_val:----------------------------|
- X * V
- X * \0hello there <1><ATTN> people\0
- X */
- X};
- X
- X#define SYMSIZ 1001
- X#ifdef VARS
- Xstruct amacro sym[SYMSIZ];
- X#else
- Xextern struct amacro sym[];
- X#endif
- X
- Xextern struct amacro *findmac();
- Xextern char *savtok();
- Xextern int gintok();
- Xextern int gtok();
- X
- X/*
- X * magicval - This (uninitialized) character is used to
- X * recognize special macro's (e.g. "defined()").
- X * An am_val field of &magicval marks a macro
- X * as special -- it cannot be undef'ed or redefined,
- X * and macro expansion in gintok() recognizes it.
- X */
- X
- X#ifdef VARS
- Xchar magicval;
- X#else
- Xextern char magicval;
- X#endif
- X
- X#define MAXPARMS 40 /* max number of formal parameters to a macro */
- X
- X/*
- X * the keyword structure - one of these describes each preprocessor keyword.
- X * see ctrl.c for the keyword array, key[].
- X */
- X
- Xstruct akeyword {
- X char *ak_name; /* name of this keyword (used to set ak_sym) */
- X int (*ak_proc)(); /* procedure to interpret this directive */
- X struct amacro *ak_sym; /*
- X * pointer to the symbol table slot for this
- X * keyword. Used to recognise the keyword.
- X * All keywords in this list are effectively
- X * "-M"ed when scpp is invoked. They are
- X * never defined.
- X * This field is initialized at runtime.
- X */
- X};
- Xextern struct akeyword *findkey();
- Xextern char *strcpy();
- SHAR_EOF
- if test 10614 -ne "`wc -c < 'scpp.h'`"
- then
- echo shar: error transmitting "'scpp.h'" '(should have been 10614 characters)'
- fi
- fi # end of overwriting check
- # End of shell archive
- exit 0
-
-